﻿using Antlr4.Runtime;
using System;
using System.Collections.Generic;

namespace ttlang.src
{
    //将AST抽象出来保存为Json,
    public class AstNode
    {
        public string type;
        public string text;
        public bool terminal;
        public List<AstNode> children;
        public int srca;
        public int srcb;
        public AstNode Cut(int start, int end)
        {
            AstNode node = new AstNode();
            node.type = this.type;

            node.terminal = this.terminal;
            node.children = new List<AstNode>();
            node.text = "";
            for (var i = start; i < end; i++)
            {
                var item = this.children[i];

                node.children.Add(item);
                node.text += item.text;
                if (i == start)
                    node.srca = item.srca;
                if (i == end - 1)
                    node.srcb = item.srcb;
            }
            return node;
        }
        public static AstNode From(Antlr4.Runtime.Tree.IParseTree antlrnode, IVocabulary tokentypes, string[] rulenames)
        {
            AstNode node = new AstNode();
            node.text = antlrnode.GetText();
            node.srca = antlrnode.SourceInterval.a;
            node.srcb = antlrnode.SourceInterval.b;
            var tnode = antlrnode as Antlr4.Runtime.Tree.ITerminalNode;
            if (tnode != null)
            {
                node.type = tokentypes.GetDisplayName(tnode.Symbol.Type);
                node.terminal = true;
            }
            else
            {
                var rnode = antlrnode as Antlr4.Runtime.RuleContext;

                if (rnode == null)
                    throw new Exception("should not null.");
                node.type = rulenames[rnode.RuleIndex];


                bool shortway = false;
                if (rnode.ChildCount == 1)
                {
                    if (node.type != "codeelem_expr"
                        && node.type != "doc"//有些还是不拆隔离的好一些
                        && node.type != "arguments")
                        shortway = true;
                }
                else if (rnode.ChildCount == 2&& rnode.GetChild(1).GetText() == ";")
                {
                    if (node.type == "code_atom")
                    {
                        shortway = true;
                    }
                    if (node.type == "code_vaild")
                    {
                        shortway = true;
                    }
                }

                if (shortway)
                {
                    var rpnode = AstNode.From(antlrnode.GetChild(0), tokentypes, rulenames);
                    node.type = rpnode.type;
                    node.terminal = rpnode.terminal;
                    node.children = rpnode.children;
                    return node;
                }
            }
            if (antlrnode.ChildCount > 0)
            {
                node.children = new List<AstNode>();
                for (var i = 0; i < antlrnode.ChildCount; i++)
                {
                    node.children.Add(AstNode.From(antlrnode.GetChild(i), tokentypes, rulenames));
                }
            }
            return node;
        }
        public Newtonsoft.Json.Linq.JObject ToJson()
        {
            Newtonsoft.Json.Linq.JObject jobj = new Newtonsoft.Json.Linq.JObject();
            jobj["type"] = type;
            jobj["text"] = text;
            jobj["terminal"] = terminal;
            jobj["srca"] = srca;
            jobj["srcb"] = srcb;
            if (children != null)
            {
                var array = new Newtonsoft.Json.Linq.JArray();
                jobj["children"] = array;
                for (var i = 0; i < children.Count; i++)
                {
                    array.Add(children[i].ToJson());
                }
            }

            return jobj;
        }
        public static AstNode FromJson(Newtonsoft.Json.Linq.JObject json)
        {
            if (json != null)
                throw new Exception("error json object");
            AstNode n = new AstNode();


            n.type = json["type"].ToString();
            n.text = json["text"].ToString();
            n.terminal = (bool)json["terminal"];
            n.srca = (int)json["srca"];
            n.srcb = (int)json["srcb"];
            if (json.ContainsKey("children"))
            {
                n.children = new List<AstNode>();
                var array = json["children"] as Newtonsoft.Json.Linq.JArray;
                for (var i = 0; i < array.Count; i++)
                {
                    n.children.Add(AstNode.FromJson(array[i] as Newtonsoft.Json.Linq.JObject));
                }
            }

            return n;
        }
    }

}
